package com.boarsoft.boar.dir.biz;

import java.util.List;

import org.hibernate.jdbc.TooManyRowsAffectedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.boarsoft.bean.ReplyInfo;
import com.boarsoft.boar.BaseConfig;
import com.boarsoft.boar.app.AppBiz;
import com.boarsoft.boar.app.AppFileBiz;
import com.boarsoft.boar.app.AppInstBiz;
import com.boarsoft.boar.common.Constants;
import com.boarsoft.boar.dir.DirBiz;
import com.boarsoft.boar.entity.AppFile;
import com.boarsoft.boar.entity.AppInfo;
import com.boarsoft.boar.entity.AppInst;
import com.boarsoft.boar.entity.DirInfo;
import com.boarsoft.boar.entity.FileInfo;
import com.boarsoft.boar.entity.LibInfo;
import com.boarsoft.boar.entity.ServerInfo;
import com.boarsoft.boar.file.FileBiz;
import com.boarsoft.boar.lib.LibBiz;
import com.boarsoft.boar.proj.ProjBiz;
import com.boarsoft.boar.server.ServerBiz;
import com.boarsoft.common.Util;
import com.boarsoft.common.dao.PagedResult;
import com.boarsoft.hibernate.biz.SimpleBizImpl;

@Component("dirBiz")
public class DirBizImpl extends SimpleBizImpl implements DirBiz {
	@Lazy
	@Autowired
	protected LibBiz libBiz;
	@Autowired
	protected AppBiz appBiz;
	@Autowired
	protected AppInstBiz appInstBiz;
	@Autowired
	protected AppFileBiz appFileBiz;
	@Autowired
	protected ProjBiz projBiz;
	@Autowired
	protected ServerBiz serverBiz;
	@Autowired
	protected FileBiz fileBiz;

	@Override
	@Transactional(readOnly = true)
	public List<DirInfo> list(String parentId) {
		StringBuilder sb = new StringBuilder();
		sb.append("and parentId='").append(parentId).append("' and status=");
		sb.append(DirInfo.STATUS_INUSE).append(" order by type, name");
		return dao.list(DirInfo.class, sb.toString());
	}

	// @Override
	// public List<DirInfo> list(String parentId, String types) {
	// StringBuilder sb = new StringBuilder();
	// sb.append("and parentId='").append(parentId).append("' and status=");
	// sb.append(DirInfo.STATUS_INUSE);
	// if (Util.strIsNotEmpty(types)) {
	// sb.append(" and type in (").append(types).append(")");
	// }
	// sb.append(" order by type, name");
	// return dao.list(DirInfo.class, sb.toString());
	// }

	@Override
	@Transactional(readOnly = true)
	public PagedResult<DirInfo> list(String parentId, String key, String orderBy, int pageNo, int pageSize) {
		StringBuilder sb = new StringBuilder();
		sb.append("and parentId='").append(parentId).append("'");
		if (Util.strIsNotEmpty(key)) {
			sb.append("and (name like '%").append(key);
			sb.append("%' or memo like '%").append(key);
			sb.append("%')");
		}
		int total = dao.getTotal(DirInfo.class, sb.toString());
		if (Util.strIsNotEmpty(orderBy)) {
			// String ob = Util.array2str(("e." + orderBy).replaceAll(",",
			// ",").split(","), ",");
			sb.append(" order by ").append(orderBy);
		} else {
			sb.append(" order by name");
		}
		List<DirInfo> lt = dao.list(DirInfo.class, sb.toString(), pageNo, pageSize);
		return new PagedResult<DirInfo>(total, lt, pageNo, pageSize);
	}

	@Override
	@Transactional
	public ReplyInfo<Object> add(DirInfo o) {
		// 检查当前目录是否有同名项，没才插入
		StringBuilder sb = new StringBuilder();
		sb.append("and parentId='").append(o.getParentId());
		sb.append("' and code='").append(o.getCode()).append("'");
		sb.append(" and type=").append(o.getType());
		if (dao.find(DirInfo.class, sb.toString()) != null) {
			return Constants.REPLY_INFO_DUPLICATED;
		}
		// 有上级就将上级目录的childCount+1
		if (!BaseConfig.DIR_ROOT.equals(o.getParentId()) && Util.strIsNotEmpty(o.getParentId())) {
			DirInfo pd = dao.get(DirInfo.class, o.getParentId());
			o.setLevel(pd.getLevel() + 1);
			o.setPath(pd.getPath().concat("/").concat(o.getParentId()));
			//
			sb.setLength(0);
			sb.append("update ").append(DirInfo.class.getName());
			sb.append(" set childCount=childCount+1 where id='");
			sb.append(o.getParentId()).append("'");
			int r = dao.executeUpdate(sb.toString());
			if (r != 1) {
				// 为了防止update多条记录，这里抛一个异常来回滚事务。
				throw new TooManyRowsAffectedException(//
						"Too many rows affected by SQL: ".concat(sb.toString()), 1, r);
			}
		}
		dao.save(o);
		return new ReplyInfo<Object>(true, o);
	}

	/**
	 * 目前path由id组成而非code组成 注释掉部分更新path的逻辑
	 */
	@Override
	@Transactional
	public ReplyInfo<Object> update(DirInfo a) {
		// DirInfo o = (DirInfo) dao.get(DirInfo.class, a.getId());
		// // 如果CODE发生改变，子目录的path都需要改变
		// if (!o.getCode().equals(a.getCode())) {
		// o.setCode(a.getCode());
		// String s = null;
		// if (BaseConfig.DIR_ROOT.equals(o.getParentId()) ||
		// Util.strIsEmpty(o.getParentId())) {
		// s = "/".concat(a.getCode());
		// } else {
		// DirInfo p = (DirInfo) dao.get(DirInfo.class, o.getParentId());
		// s = String.format("%s/%s", p.getPath(), o.getId());
		// }
		// if (o.getChildCount() > 0) {
		// StringBuilder sb = new StringBuilder();
		// sb.append("update ").append(DirInfo.class.getName());
		// sb.append(" set path = concat('").append(s);
		// sb.append("', substring(path, ").append(s.length());
		// sb.append(", length(path) － ").append(s.length());
		// sb.append(")) where path like '").append(o.getPath()).append("%'");
		// log.info(sb.toString());
		// dao.executeUpdate(sb.toString());
		// }
		// o.setPath(s);
		// }
		// o.setName(a.getName());
		// o.setMemo(a.getMemo());
		dao.update(a);
		return new ReplyInfo<Object>(true, a);
	}

	@Override
	@Transactional
	public void delete(String id) throws Exception {
		DirInfo d = this.get(id);
		if (d == null) {
			return;
		}
		this.delete(d);
	}

	@Override
	@Transactional
	public void delete(DirInfo o) throws Exception {
		dao.delete(o);
		switch (o.getType()) {
		case DirInfo.TYPE_DIR:
		case DirInfo.TYPE_COMPANY:
		case DirInfo.TYPE_DEPT:
			break;
		case DirInfo.TYPE_PROJ:
			projBiz.delete(o.getId());
			break;
		case DirInfo.TYPE_LIB:
			libBiz.delete(o.getId());
			break;
		case DirInfo.TYPE_APP:
			appBiz.delete(o.getId());
			break;
		default:
			break;
		}
		// 使用DirBizExAspect切面方式实现以解耦deploy和soagov
		// if (dirExBiz != null) {
		// dirExBiz.delete(o.getType(), o.getId());
		// }
		if (o.getChildCount() > 0) {
			List<DirInfo> lt = this.list(o.getId());
			if (lt == null || lt.size() == 0) {
				log.warn("{} has no children but child count is {} ", o, o.getChildCount());
				return;
			}
			for (DirInfo d : lt) {
				this.delete(d);
			}
		}
	}

	@Override
	@Transactional(readOnly = true)
	public DirInfo get(String id) {
		return dao.get(DirInfo.class, id);
	}

	@Override
	@Transactional(readOnly = true)
	public List<DirInfo> listByPath(String path) {
		StringBuilder sb = new StringBuilder();
		sb.append("and path='").append(path).append("'");
		return (List<DirInfo>) dao.list(DirInfo.class, sb.toString());
	}

	// TODO 不要用
	private String getTypeDesc(short type) {
		String desc = "";
		switch (type) {
		case DirInfo.TYPE_COMPANY:
			desc = desc + type + "-" + "公司";
			break;
		case DirInfo.TYPE_DEPT:
			desc = desc + type + "-" + "部门";
			break;
		case DirInfo.TYPE_PROJ:
			desc = desc + type + "-" + "系统或工程";
			break;
		case DirInfo.TYPE_DIR:
			desc = desc + type + "-" + "目录";
			break;
		case DirInfo.TYPE_LIB:
			desc = desc + type + "-" + "库包";
			break;
		case DirInfo.TYPE_APP:
			desc = desc + type + "-" + "应用";
			break;
		default:
			desc = desc + type + "-" + "其他";
			break;
		}
		return desc;
	}

	@Override
	@Transactional
	public DirInfo copy(DirInfo od, String to) throws Exception {
		DirInfo nd = (DirInfo) this.transfer(od, DirInfo.class);// 把od实体复制成一个新的实体
		DirInfo pd = this.get(to);
		nd.setId(null);
		if (BaseConfig.DIR_ROOT.equalsIgnoreCase(to) || Util.strIsEmpty(to)) {
			nd.setParentId(BaseConfig.DIR_ROOT);
			nd.setPath("/");
		}
		nd.setParentId(pd.getId());
		nd.setPath(pd.getPath() + "/" + pd.getId());
		nd.setLevel(pd.getLevel() + 1);
		dao.save(nd);

		pd.setChildCount(pd.getChildCount() + 1);
		dao.update(pd);
		log.debug("od={},pd={},nd={}", JSON.toJSONString(od), JSON.toJSONString(pd), JSON.toJSONString(nd));

		switch (od.getType()) {
		case DirInfo.TYPE_DIR:
			break;
		case DirInfo.TYPE_LIB:
			libBiz.copy(od.getId(), to, nd.getId());
			break;
		case DirInfo.TYPE_APP:
			appBiz.copy(od.getId(), to, nd.getId());
			break;
		case DirInfo.TYPE_PROJ:
			projBiz.copy(od.getId(), to, nd.getId());
			break;
		case DirInfo.TYPE_DEPT:
			break;
		case DirInfo.TYPE_COMPANY:
			break;
		case DirInfo.TYPE_FILE:
			fileBiz.copy(od.getId(), to, nd.getId());
			break;
		default:
			// 使用DirBizExAspect切面方式实现以解耦deploy和soagov
			// dirExBiz.copy(od.getType(), od.getId(), to, nd.getId());
			break;
		}
		dao.flush();
		return nd;
	}

	/**
	 * TODO 1，递归修改目录上的对象的path
	 * 2，修改app_info、lib_info、server_info、file_info表projId（由原来的proj移动到新的proj）
	 *
	 * @param targetId
	 * @param projId
	 */
	private void updateTarget(String targetId, String copyId, String projId) {
		// 查询被修改目标对象在dir_info表中的数据
		DirInfo dirInfo = this.get(targetId);
		if (dirInfo == null) {
			log.info("根据id：{},在dir_info查询待修改对象返回为空！", targetId);
			return;
		}
		String oldPath = dirInfo.getPath();
		DirInfo pDirInfo = this.get(copyId);
		String newPath = pDirInfo.getPath() + "/" + pDirInfo.getId();
		dirInfo.setPath(newPath);
		dao.merge(dirInfo);

		short type = dirInfo.getType();
		switch (type) {
		case DirInfo.TYPE_LIB:
			LibInfo lib = libBiz.get(targetId);
			DirInfo d = this.get(targetId);
			lib.setProjId(projId);
			libBiz.update(d, lib);
			break;
		case DirInfo.TYPE_APP:
			AppInfo app = appBiz.get(targetId);
			DirInfo dd = this.get(targetId);
			app.setProjId(projId);
			appBiz.update(dd, app);
			List<AppInst> aiLt = appInstBiz.list(null, targetId, null, null);
			for (AppInst inst : aiLt) {
				String serverId = inst.getServerId();
				ServerInfo serverInfo = serverBiz.get(serverId);
				serverInfo.setProjId(projId);
				serverBiz.save(serverInfo);
			}
			List<AppFile> appFiles = appFileBiz.find(targetId, null, null);
			for (AppFile appFile : appFiles) {
				String fileId = appFile.getFileId();
				FileInfo fileInfo = fileBiz.get(fileId);
				fileInfo.setProjId(projId);
				fileBiz.save(null, fileInfo);
			}
			break;
		default:
			log.info("目标类型为：{}，跳过目标（不更新projId）！", type);
			break;
		}

		log.info("当前修改目标对象:{},类型为：{},修改前path={},修改后path={}", dirInfo.getName(), getTypeDesc(type), oldPath, dirInfo.getPath());
		// 获取被修改目标对象其下子节点个数，后续根据这个值决定是否递归修改
		int childCount = dirInfo.getChildCount();
		if (childCount > 0) {
			log.info("当前修改目标下还有有子节点！子节点个数为：{}，将进行递归修改path！", childCount);
			List<DirInfo> list = list(targetId);
			if (list == null || list.size() == 0) {
				String msg = "当前修改的目标dir(id=" + targetId + ")childCount=" + childCount
						+ ",但查询返回其下子dir为null！向下递归修改path失败！";
				log.info(msg);
				return;
			}
			for (DirInfo dir : list) {
				updateTarget(dir.getId(), targetId, projId);
			}
		} else {
			log.info("当前被修改目标下已没有子节点！");
		}
	}

	/**
	 * @param d
	 * @param to
	 */
	@Override
	@Transactional
	public boolean move(DirInfo d, String to) {
		// 修改原父节点的childCount
		String opid = d.getParentId();
		if (!BaseConfig.DIR_ROOT.equalsIgnoreCase(opid) && Util.strIsNotEmpty(opid)) {
			DirInfo op = this.get(opid);
			op.setChildCount(op.getChildCount() - 1);// 从移往的childCount-1
			dao.merge(op);
		}
		d.setParentId(to);

		if (!BaseConfig.DIR_ROOT.equalsIgnoreCase(to) && Util.strIsNotEmpty(to)) {
			DirInfo np = this.get(to);
			np.setChildCount(np.getChildCount() + 1);// 被移往的childCount+1
			dao.merge(np);
			//
			d.setLevel(np.getLevel() + 1);
			dao.merge(d);

			// 查找目的地的projId，用于更新 app_info、lib_info、server_info、file_info表projId
			String projId = this.lookup(np, DirInfo.TYPE_PROJ);
			if (projId != null) {
				updateTarget(d.getId(), to, projId);
			}
		}

		return true;
	}

	private boolean startAppOrSys(String id) {
		boolean ret = false;
		String[] IDS = id.split(",");
		for (String ID : IDS) {
			DirInfo d = (DirInfo) dao.get(DirInfo.class, ID);
			if (d == null) {
				log.info("启用应用或系统操作dir_info表失败，原因：根据应用或系统id={},查询应用或系统返回为空！", ID);
			}
			if (d.getStatus() != DirInfo.STATUS_NOUSE) {
				log.info("应用或系统id={},当前已处于启用状态，无需再次启用！", ID);
				ret = true;
			} else {
				d.setStatus(DirInfo.STATUS_INUSE);
				ret = true;
			}
		}
		return ret;
	}

	@Override
	@Transactional
	public boolean start(String id) {
		return startAppOrSys(id);
	}

	private boolean stopAppOrSys(String id) {
		boolean ret = false;
		String[] IDS = id.split(",");
		for (String ID : IDS) {
			DirInfo d = (DirInfo) dao.get(DirInfo.class, ID);
			if (d == null) {
				log.info("停用应用或系统操作dir_info表失败！原因：根据应用或系统id={}查询应用或系统返回为空！", ID);
				continue;
			}
			if (d.getStatus() != DirInfo.STATUS_INUSE) {
				log.info("应用或系统id={},当前已处于停用状态，无需再次停用！", ID);
				ret = true;
			} else {
				d.setStatus(DirInfo.STATUS_NOUSE);
				ret = true;
			}
		}

		return ret;
	}

	@Override
	@Transactional
	public boolean stop(String id) {
		return stopAppOrSys(id);
	}

	@Override
	@Transactional(readOnly = true)
	public PagedResult<DirInfo> listSys(String id, short type, String key, String orderBy, int pageNo, int pageSize) {
		StringBuilder sb = new StringBuilder();
		sb.append("and parentId='").append(id).append("'").append(" and type=").append(type);
		if (Util.strIsNotEmpty(key)) {
			sb.append(" and (name like '%").append(key);
			sb.append("%' or memo like '%").append(key);
			sb.append("%')");
		}
		int total = dao.getTotal(DirInfo.class, sb.toString());
		if (Util.strIsNotEmpty(orderBy)) {
			// String ob = Util.array2str(("e." + orderBy).replaceAll(",",
			// ",").split(","), ",");
			sb.append(" order by ").append(orderBy);
		} else {
			sb.append(" order by name");
		}
		List<DirInfo> lt = dao.list(DirInfo.class, sb.toString(), pageNo, pageSize);
		return new PagedResult<DirInfo>(total, lt, pageNo, pageSize);
	}

	@Override
	@Transactional(readOnly = true)
	public String lookup(DirInfo d, short type) {
		if (type == d.getType()) {
			return d.getId();
		}
		String pa[] = d.getPath().split("/");
		for (int i = pa.length - 1; i > 0; i--) {
			DirInfo a = dao.get(DirInfo.class, pa[i]);
			if (type == a.getType()) {
				return pa[i];
			}
		}
		return null;
	}

	@Override
	@Transactional(readOnly = true)
	public List<DirInfo> list(String ancestorId, short type) {
		DirInfo d = this.get(ancestorId);
		StringBuilder sb = new StringBuilder();
		sb.append("and path like '").append(d.getPath()).append("/")//
				.append(d.getId()).append("/%' and type=").append(type);
		return dao.list(DirInfo.class, sb.toString());
	}
}